import java.net.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.io.*;

public class calculatorServer {
	public static void main(String[] args) throws IOException {

		int port = 4321;
		DatagramSocket socket = new DatagramSocket(port);

		//创建任务的输入队列 in，输出队列 out
		LinkedBlockingQueue<DatagramPacket> in = new LinkedBlockingQueue<DatagramPacket>(10);
		LinkedBlockingQueue<DatagramPacket> out = new LinkedBlockingQueue<DatagramPacket>(10);

		//统计各运算次数 counter
		statistics counter = new statistics();

		//计算线程 generator
		computeThreadGenerator generator = new computeThreadGenerator(in, out, counter);
		//发送线程 send 
		sendThread send = new sendThread(out);

		Thread generatorThread = new Thread(generator);
		Thread sendThread = new Thread(send);
		generatorThread.start();
		sendThread.start();

		System.out.println("calculatorServer listening port: "+port);

		//将任务放入输入队列
		try {
			while (true) {
				// Thread.sleep(3000);
				byte[] m = new byte[200];
				DatagramPacket receive = new DatagramPacket(m, m.length);
				socket.receive(receive);
				in.put(receive);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			socket.close();
		}

	}
}

/*
	将输入队列的任务取出，并创建线程完成计算
*/
class computeThreadGenerator implements Runnable {

	private LinkedBlockingQueue<DatagramPacket> in;
	private LinkedBlockingQueue<DatagramPacket> out;
	private statistics s;
	private DatagramPacket packet;

	public computeThreadGenerator(LinkedBlockingQueue<DatagramPacket> in, LinkedBlockingQueue<DatagramPacket> out, statistics s) {
		this.in = in;
		this.out = out;
		this.s = s;
	}

	@Override
	public void run() {
		try {
			while (true) {
				// Thread.sleep(5000);
				this.packet = this.in.take();
				computeThread compute = new computeThread(out, packet, s);
				Thread thread = new Thread(compute);
				thread.start();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

/*
	做算术运算的线程
	并将算术运算结果放入输出队列 out
*/
class computeThread implements Runnable {
	private LinkedBlockingQueue<DatagramPacket> out;
	private statistics s;
	private DatagramPacket packet;

	public computeThread(LinkedBlockingQueue<DatagramPacket> queue, DatagramPacket p, statistics s) {
		this.out = queue;
		this.packet = p;
		this.s = s;
	}

	@Override
	public void run() {
		//将数据分解成有效字段
		dataDecompose decompose = new dataDecompose(new String(this.packet.getData()),"\n");
		//运算统计
		this.s.count(decompose.getOperator());
		//运算
		calculate cal = new calculate(decompose.getOperator(), decompose.getFirst(), decompose.getSecond());
		byte[] m;
		//判断运算是否出错
		if(cal.isError() == false){
			m = String.valueOf(cal.getResult()).getBytes();
		} else {
			m = "err".getBytes();
		}
		
		DatagramPacket toSend = new DatagramPacket(m, m.length, this.packet.getAddress(), this.packet.getPort());
		try {
			this.out.put(toSend);
			//打印各运算的统计
			System.out.println("calculatorServer operation statistics: \n"+"plusOperation: "+this.s.getPlusCount()+
				"  subOperation: "+this.s.getSubCount()+"  multiOperation: "+
				this.s.getMultiCounter()+"  divideOperation: "+this.s.getDivideCount());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

/*
	发送线程 
	将输出队列的任务发送
	输出队列 out
*/
class sendThread implements Runnable {
	private LinkedBlockingQueue<DatagramPacket> out;
	private DatagramPacket packet;

	public sendThread(LinkedBlockingQueue<DatagramPacket> queue) {
		this.out = queue;
	}

	@Override
	public void run() {
		DatagramSocket socket;
		try {
			socket = new DatagramSocket();
			while (true) {
				// Thread.sleep(8000);
				this.packet = out.take();
				socket.send(packet); 
			}
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

/*
	做算术运算 
	操作符 Operator
	第一个参数 first
	第二个参数 second
	运算结果 result
*/
class calculate {
	private double first;
	private double second;
	private char operator;
	private double result;
	private boolean error;

	public calculate(char o, double f, double s) {
		this.first = f;
		this.second = s;
		this.operator = o;
		this.solve();
	}

	public double getResult() {
		return this.result;
	}

	public boolean isError(){
		return this.error;
	}

	private void solve() {
		switch (this.operator) {
			case '+':
				this.result = this.first + this.second;
				break;
			case '-':
				this.result = this.first - this.second;
				break;
			case '*':
				this.result = this.first * this.second;
				break;
			case '/':
				if(this.second == 0) {this.error = true; break;}	//除数为零
				this.result = this.first / this.second;
				break;
			default:
				this.error = true;
				break;
		}
	}
}

/*
	加减乘除运算次数统计
	加法次数 plusCounter
	减法次数 subCounter
	乘法次数 multiCounter
	除法次数 divideCounter
*/

class statistics {
	private AtomicInteger plusCounter = new AtomicInteger(0);
	private AtomicInteger subCounter = new AtomicInteger(0);
	private AtomicInteger multiCounter = new AtomicInteger(0);
	private AtomicInteger divideCounter = new AtomicInteger(0);

	public void count(char operator){
		switch(operator){
			case '+':
				this.plusCounter.incrementAndGet();
				break;
			case '-':
				this.subCounter.incrementAndGet();
				break;
			case '*':
				this.multiCounter.incrementAndGet();
				break;
			case '/':
				this.divideCounter.incrementAndGet();
				break;
			default :
				break;
		}
	}

	public int getPlusCount() {
		return this.plusCounter.get();
	}

	public int getSubCount() {
		return this.subCounter.get();
	}

	public int getMultiCounter() {
		return this.multiCounter.get();
	}

	public int getDivideCount() {
		return this.divideCounter.get();
	}
}

/*
	将接收到的String类型数据分解为操作符，第一个参数，第二个参数

	操作符 Operator
	第一个参数 first
	第二个参数 second
*/
class dataDecompose {
	private char operator;
	private Double first;
	private Double second;

	public dataDecompose(String str,String separator) {
		String[] part = str.split(separator);
		this.operator = part[0].toCharArray()[0];
		this.first = Double.parseDouble(part[1]);
		this.second = Double.parseDouble(part[2]); 
	}

	public char getOperator() {
		return this.operator;
	}

	public Double getFirst() {
		return this.first;
	}

	public Double getSecond() {
		return this.second;
	}
}
